#ifndef NETMAN_H_INCLUDED
#define NETMAN_H_INCLUDED
#ifdef _WIN32
#include <WinSock2.h>
#include <cmath>
#pragma comment (lib, "ws2_32.lib")
#endif

//types
#define INT		int
#define WORD	unsigned short int
#define DWORD	unsigned int
#define CHAR	char
#define BYTE	unsigned char
#define ECANTFINDHOSTNAME 0x1233L

#include <list>
#include <string>
#include <iostream>

class TCPMan;
class TCPConnection;
class TCPServer;
class TCPPacketServer;
class TCPConnectionPackets;

//Two function for extension of classes TCPMan, TCPConnection, TCPServer
typedef TCPServer *(*onCreateServer)(TCPMan *pManager, WORD wPort);
typedef TCPConnection *(*onCreateConnection)(TCPMan *pManager, TCPServer *pServer,
											const char *szAddress, WORD wPort);

//Standard fabrics
TCPServer * _onCreateServer(TCPMan *pManager, WORD wPort);
TCPConnection *_onCreateConnection(TCPMan *pManager, TCPServer *pServer, 
								   const char *szAddress, WORD wPort);

//Hi-level fabrics
TCPConnection *ConnectionPackFabric(TCPMan *pManager, TCPServer *pServer,
									const char *szAddress, WORD wPort);
TCPServer *ServerPackFabric(TCPMan *pManager, WORD wPort);

class TCPMan
{
	friend TCPConnection;
	friend TCPServer;
public:
	TCPMan(onCreateConnection pConnectionFabric = _onCreateConnection, 
		onCreateServer pServerFabric = _onCreateServer);
	virtual ~TCPMan();
	TCPConnection *Connect(const char *szAddr, WORD wPort);
	TCPServer *CreateServer(WORD wPort);

	void DeleteConnection(TCPConnection *pClient);
	void DeleteServer(TCPServer *pServer);

	void Update();		
	bool IsWork(){ return m_fIsWork;};

protected:
	virtual void				OnError(int iError, const char *szDescription){
		std::cout << "An error "<< iError<<" has occurred: "<<szDescription<<std::endl;};
	std::list<TCPConnection*>	m_pConnections;
	std::list<TCPServer*>		m_pServers;
	bool						m_fIsWork;
	bool						TryUp();
	void						TryDown();
	
	onCreateServer				m_CreateServer;
	onCreateConnection			m_CreateConnection;

};

class TCPConnection
{
	friend TCPMan;
	friend TCPServer;
public:
	TCPConnection(const char *szAddress, WORD wPort, TCPMan *pManager);
	virtual ~TCPConnection();

	int Send(const char *szMessage, DWORD dwSize);
	void Update();
	bool IsConnect();

protected:
	bool TryToConnect();
	bool TryToDisconnect();
	bool TryToReceive();

	virtual void OnReceive(const char *pData, WORD wSize){
		std::cout.write(pData, wSize);
	};
	virtual void OnConnect(){
		std::cout<<"Socket has connected to server!"<<std::endl;
	};
	virtual void OnDisconnect(){
		std::cout<<"Socket was disconnected!"<<std::endl;
	};
	virtual void OnError(long lError, const char *szErrorDesc){
		m_pManager->OnError(lError, szErrorDesc);
	};

	std::string	m_sAddress;
	WORD		m_wPort;
	TCPMan		*m_pManager;
	std::list<BYTE*>	m_Messages;

	TCPServer*	m_pServer;		//Did socket create for server?
	unsigned long	m_GetBufferLength();
private:
	SOCKET		m_Socket;
	bool		m_SafeSocket();	//Create TCP/IP socket. If
	bool		m_SafeBind();
	bool		m_SafeConnect();
	

	bool		m_fIsSocked;		//Did sock successful?
	bool		m_fIsBinded;		//Did bind successful?
	bool		m_fIsConnected;		//Did connect successful?

	
	
};

class TCPServer
{
	friend TCPMan;
	friend TCPConnection;
public:
	TCPServer(WORD wPort, TCPMan *pManager);
	virtual ~TCPServer();

	void CloseConnection(TCPConnection *pConnection);
	void Update();
	std::list<TCPConnection*>	&GetClients(){return m_aClients;};
protected:
	TCPMan		*m_pManager;
	void Down();
	bool TryUp();

	virtual void OnServerUp(){
		std::cout<<"Server up!"<<std::endl;
	};
	virtual void OnServerDown(){
		std::cout<<"Server is down!"<<std::endl;
	};
	virtual void OnAccept(TCPConnection *pClient){
		std::cout<<"Server accepted client!"<<std::endl;
	}
	virtual void OnClose(TCPConnection *pClient){
		std::cout<<"Client aborted connection"<<std::endl;};
	virtual void OnReceive(TCPConnection *pSender,	//Work as echo service
		const char *pData, DWORD dwDataSize){
			std::cout.write(pData, dwDataSize);};
	virtual void OnError(long lError, const char *szErrorDesc){
		m_pManager->OnError(lError, szErrorDesc);};

	//Exported from TCPConnection
	bool		m_SafeSocket();	//Create TCP/IP socket. If
	bool		m_SafeBind();	
	bool		m_SafeListen();	//If is there connections in the queue, then adding they to m_aClients
	SOCKET		m_SafeAccept();	/*Add new TCPConnection to queue. 
								Return socket if successful, INVALID_SOCKET - if queue is empty or failed*/
	TCPConnection*	m_ConvertSocket(SOCKET sockNewbie);	//Convert SOCKET to our TCPConnection

	//Server's settings
	DWORD m_wPort;
	std::list<TCPConnection*>	m_aClients;
private:
	bool		m_fIsSocked;		//Did sock successful?
	bool		m_fIsBinded;		//Did bind successful?
	bool		m_fIsListen;		//Did connect successful?

	SOCKET		m_Socket;
};


//Hi-level classes

class TCPPacketServer: public TCPServer
{
public:
	TCPPacketServer(WORD wPort, TCPMan *pManager):TCPServer(wPort, pManager){;};
	virtual void OnPack(TCPConnectionPackets *pSender, const char *szData, WORD wSize)
	{
		std::cout<<"Packet received. Size: "<<wSize<<". "<<std::endl;
		std::cout.write(szData, wSize);
		std::cout<<std::endl;
	};
};
class TCPConnectionPackets: public TCPConnection
{
public:
	TCPConnectionPackets(const char *szAddress, WORD wPort, TCPMan *pManager):
	  TCPConnection(szAddress, wPort, pManager){;};
	  bool SendPack(const char *c_szData, unsigned short int wLength);
	  void OnReceive(const char *pData, WORD wSize);
	  virtual void OnPack(const char *pPacket, unsigned short int wSize);
protected:
private:
	WORD m_wFirstHeaderByte;		//When header is spliced. It is first byte		
	WORD m_wSecondHeaderByte;		//When header is spliced. It is second byte		
	//std::string	m_sName;
	//m_pServer will TCPPacketServer
};


#endif